/*
 * 
 * 
 * 
 */
package com.hboxs.asl.service.impl;

import com.hboxs.asl.CustomPriceComparator;
import com.hboxs.asl.CustomProductCategoryComparator;
import com.hboxs.asl.Page;
import com.hboxs.asl.Pageable;
import com.hboxs.asl.dao.ArticleDao;
import com.hboxs.asl.dao.ProductDao;
import com.hboxs.asl.entity.Article;
import com.hboxs.asl.entity.Attribute;
import com.hboxs.asl.entity.Product;
import com.hboxs.asl.entity.Product.OrderType;
import com.hboxs.asl.service.SearchService;
import net.paoding.analysis.analyzer.PaodingAnalyzer;
import org.apache.commons.lang.StringUtils;
import org.apache.lucene.analysis.Analyzer;
import org.apache.lucene.index.Term;
import org.apache.lucene.queryParser.ParseException;
import org.apache.lucene.queryParser.QueryParser;
import org.apache.lucene.search.BooleanClause.Occur;
import org.apache.lucene.search.*;
import org.apache.lucene.util.Version;
import org.hibernate.search.jpa.FullTextEntityManager;
import org.hibernate.search.jpa.FullTextQuery;
import org.hibernate.search.jpa.Search;
import org.hibernate.search.query.dsl.QueryBuilder;
import org.hibernate.search.query.facet.Facet;
import org.hibernate.search.query.facet.FacetSortOrder;
import org.hibernate.search.query.facet.FacetingRequest;
import org.json.JSONArray;
import org.json.JSONObject;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.wltea.analyzer.lucene.IKAnalyzer;

import javax.annotation.Resource;
import javax.persistence.EntityManager;
import javax.persistence.PersistenceContext;
import java.io.BufferedReader;
import java.io.InputStream;
import java.io.InputStreamReader;
import java.net.URL;
import java.net.URLConnection;
import java.net.URLEncoder;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;

/**
 * Service - 搜索
 */
@Service("searchServiceImpl")
@Transactional
public class SearchServiceImpl implements SearchService {

    /**
     * 模糊查询最小相似度
     */
    private static final float FUZZY_QUERY_MINIMUM_SIMILARITY = 0.5F;

    @PersistenceContext
    protected EntityManager entityManager;
    @Resource(name = "articleDaoImpl")
    private ArticleDao articleDao;
    @Resource(name = "productDaoImpl")
    private ProductDao productDao;

    public void index() {
        index(Article.class);
        index(Product.class);
    }

    public void index(Class<?> type) {
        FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
        if (type == Article.class) {
            for (int i = 0; i < articleDao.count(); i += 20) {
                List<Article> articles = articleDao.findList(i, 20, null, null);
                for (Article article : articles) {
                    fullTextEntityManager.index(article);
                }
                fullTextEntityManager.flushToIndexes();
                fullTextEntityManager.clear();
                articleDao.clear();
                fullTextEntityManager.close();
            }
        } else if (type == Product.class) {
            for (int i = 0; i < productDao.count(); i += 20) {
                List<Product> products = productDao.findList(i, 20, null, null);
                for (Product product : products) {
                    fullTextEntityManager.index(product);
                }
                fullTextEntityManager.flushToIndexes();
                fullTextEntityManager.clear();
                productDao.clear();
                fullTextEntityManager.close();
            }
        }
    }

    public void index(Article article) {
        if (article != null) {
            FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
            fullTextEntityManager.index(article);
            fullTextEntityManager.close();
        }
    }

    public void index(Product product) {
        if (product != null) {
            FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
            fullTextEntityManager.index(product);
            fullTextEntityManager.close();
        }
    }

    public void purge() {
        purge(Article.class);
        purge(Product.class);
    }

    public void purge(Class<?> type) {
        FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
        if (type == Article.class) {
            fullTextEntityManager.purgeAll(Article.class);
            fullTextEntityManager.close();
        } else if (type == Product.class) {
            fullTextEntityManager.purgeAll(Product.class);
            fullTextEntityManager.close();
        }
    }

    public void purge(Article article) {
        if (article != null) {
            FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
            fullTextEntityManager.purge(Article.class, article.getId());
            fullTextEntityManager.close();
        }
    }

    public void purge(Product product) {
        if (product != null) {
            FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
            fullTextEntityManager.purge(Product.class, product.getId());
            fullTextEntityManager.close();
        }
    }

    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true)
    public Page<Article> search(String keyword, Pageable pageable) {
        if (StringUtils.isEmpty(keyword)) {
            return new Page<Article>();
        }
        if (pageable == null) {
            pageable = new Pageable();
        }
        try {
            String text = QueryParser.escape(keyword);
            QueryParser titleParser = new QueryParser(Version.LUCENE_35, "title", new IKAnalyzer());
            titleParser.setDefaultOperator(QueryParser.AND_OPERATOR);
            Query titleQuery = titleParser.parse(text);
            FuzzyQuery titleFuzzyQuery = new FuzzyQuery(new Term("title", text), FUZZY_QUERY_MINIMUM_SIMILARITY);
            Query contentQuery = new TermQuery(new Term("content", text));
            Query isPublicationQuery = new TermQuery(new Term("isPublication", "true"));
            BooleanQuery textQuery = new BooleanQuery();
            BooleanQuery query = new BooleanQuery();
            textQuery.add(titleQuery, Occur.SHOULD);
            textQuery.add(titleFuzzyQuery, Occur.SHOULD);
            textQuery.add(contentQuery, Occur.SHOULD);
            query.add(isPublicationQuery, Occur.MUST);
            query.add(textQuery, Occur.MUST);
            FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
            FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(query, Article.class);
            fullTextQuery.setSort(new Sort(new SortField("isTop", SortField.STRING, true), new SortField(null, SortField.SCORE),
                    new SortField("createDate", SortField.LONG, true)));
            fullTextQuery.setFirstResult((pageable.getPageNumber() - 1) * pageable.getPageSize());
            fullTextQuery.setMaxResults(pageable.getPageSize());
            return new Page<Article>(fullTextQuery.getResultList(), fullTextQuery.getResultSize(), pageable);
        } catch (ParseException e) {
            e.printStackTrace();
        }
        return new Page<Article>();
    }

    @SuppressWarnings("unchecked")
    @Transactional(readOnly = true)
    public Page<Product> search(String keyword, Long brId, Map<Attribute, String> attributeValue, Integer startPrice, Integer endPrice, OrderType orderType, Pageable pageable) {
        if (StringUtils.isEmpty(keyword)) {
            return new Page<>();
        }
        if (pageable == null) {
            pageable = new Pageable();
        }
        FullTextEntityManager fullTextEntityManager = Search.getFullTextEntityManager(entityManager);
        try {
            String text = QueryParser.escape(keyword);

            Analyzer paodingAnalyzer = new PaodingAnalyzer();
            // name
            QueryParser nameParser = new QueryParser(Version.LUCENE_35, "name", paodingAnalyzer);
            nameParser.setDefaultOperator(QueryParser.AND_OPERATOR);
            Query nameQuery = nameParser.parse(text);
            FuzzyQuery nameFuzzyQuery = new FuzzyQuery(new Term("name", text), FUZZY_QUERY_MINIMUM_SIMILARITY);

            // pinyin
            QueryParser pinyinParser = new QueryParser(Version.LUCENE_35, "pinyin", paodingAnalyzer);
            pinyinParser.setDefaultOperator(QueryParser.AND_OPERATOR);
            Query pinyinQuery = pinyinParser.parse(text);
            FuzzyQuery pinyinFuzzyQuery = new FuzzyQuery(new Term("pinyin", text), FUZZY_QUERY_MINIMUM_SIMILARITY);

            BooleanQuery textQuery = new BooleanQuery();
            BooleanQuery query = new BooleanQuery();

            textQuery.add(nameQuery, Occur.SHOULD);
            textQuery.add(nameFuzzyQuery, Occur.SHOULD);
            textQuery.add(pinyinQuery, Occur.SHOULD);
            textQuery.add(pinyinFuzzyQuery, Occur.SHOULD);

            query.add(textQuery, Occur.MUST);

            // brand
            if (brId != null && !StringUtils.isEmpty(brId.toString())) {
                TermQuery brandQuery = new TermQuery(new Term("brand", brId.toString()));
                query.add(brandQuery, Occur.MUST);
            }

            // attribute
            if (attributeValue != null) {
                for (Entry<Attribute, String> entry : attributeValue.entrySet()) {
                    String propertyName = Product.ATTRIBUTE_VALUE_PROPERTY_NAME_PREFIX + entry.getKey().getPropertyIndex();
                    TermQuery attributeQuery = new TermQuery(new Term(propertyName, entry.getValue()));
                    query.add(attributeQuery, Occur.MUST);
                }
            }

            FullTextQuery fullTextQuery = fullTextEntityManager.createFullTextQuery(query, Product.class);

            // 得到最大分类的Facet
            QueryBuilder queryBuilder = fullTextEntityManager.getSearchFactory().buildQueryBuilder().forEntity(Product.class).get();
            FacetingRequest categoryFacetingRequest = queryBuilder.facet().name("productCategoryFacet").onField("productCategory")
                    .discrete()
                    .orderedBy(FacetSortOrder.COUNT_DESC)
                    .includeZeroCounts(false)
                    .createFacetingRequest();

            fullTextQuery.getFacetManager().enableFaceting(categoryFacetingRequest);
            List<Facet> facets = fullTextQuery.getFacetManager().getFacets("productCategoryFacet");
            if (null == facets || facets.size() == 0) {
                return null;
            }
            Facet productCategoryFacet = facets.get(0);

            // sort
            SortField[] sortFields = new SortField[5];

            sortFields[0] = new SortField("isTop", SortField.STRING, true);
            if (null == startPrice)
                startPrice = 0;
            if (null == endPrice)
                endPrice = 0;
            if (startPrice.compareTo(endPrice) > 0) {
                Integer temp = startPrice;
                startPrice = endPrice;
                endPrice = temp;
            }
            sortFields[1] = new SortField("price", new CustomPriceComparator(startPrice, endPrice), true);

            // CustomProductComparator 自定义排序，把分类最多的product放前面
            if (orderType == OrderType.priceAsc) {
                sortFields[2] = new SortField("price", SortField.DOUBLE, false);
            } else if (orderType == OrderType.priceDesc) {
                sortFields[2] = new SortField("price", SortField.DOUBLE, true);
            } else if (orderType == OrderType.salesDesc) {
                sortFields[2] = new SortField("sales", SortField.INT, true);
            } else if (orderType == OrderType.dateDesc) {
                sortFields[2] = new SortField("createDate", SortField.LONG, true);
            } else {
                // 默认用创建时间？
                sortFields[2] = new SortField("createDate", SortField.LONG, true);
            }
            sortFields[3] = new SortField("productCategory", new CustomProductCategoryComparator(productCategoryFacet), true);
            sortFields[4] = new SortField("score", SortField.INT, true);

            fullTextQuery.setSort(new Sort(sortFields));
            fullTextQuery.setFirstResult((pageable.getPageNumber() - 1) * pageable.getPageSize());
            fullTextQuery.setMaxResults(pageable.getPageSize());

            return new Page<Product>(fullTextQuery.getResultList(), fullTextQuery.getResultSize(), pageable);
        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            fullTextEntityManager.close();
        }
        return new Page<Product>();
    }

    /**
     * 搜索快捷提示
     */
    @RequestMapping(value = "/sugg", method = RequestMethod.GET)
    public String[] suggestSearch(String keyword) {
        URL url;
        try {
            url = new URL("http://suggest.taobao.com/sug?code=utf-8&q=" + URLEncoder.encode(keyword, "utf-8"));

            // 返回一个 URLConnection 对象，它表示到 URL 所引用的远程对象的连接。
            URLConnection uc = url.openConnection();
            // 打开的连接读取的输入流。
            InputStream in = uc.getInputStream();
            BufferedReader reader = new BufferedReader(new InputStreamReader(in, "utf-8"));
            String line;
            StringBuffer pageBuffer = new StringBuffer();
            while ((line = reader.readLine()) != null) {
                pageBuffer.append(line);
            }
            // 解析taobao的接口数据
            JSONObject resultJson = new JSONObject(pageBuffer.toString());
            JSONArray resultJsonArray = resultJson.getJSONArray("result");
            String[] suggKeys = new String[resultJsonArray.length()];
            for (int i = 0; i < resultJsonArray.length(); i++) {
                // ["杯子","830335"],["杯子陶瓷","188723"]
                suggKeys[i] = (String) ((JSONArray) resultJsonArray.get(i)).get(0);
            }
            return suggKeys;
        } catch (Exception e) {
            e.printStackTrace();
        }
        return null;
    }

}